package saxon.gui.model;

import com.google.common.collect.Lists;
import java.io.File;
import java.io.FileFilter;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Semaphore;
import javax.swing.SwingUtilities;
import net.sf.saxon.s9api.XdmValue;
import saxon.gui.model.swing.SwingXWorker;
import saxon.gui.model.xml.XPathHelper;
import saxon.gui.model.xml.XQueryHelper;
import saxon.gui.model.xml.XmlHelper;
import saxon.gui.view.api.XView;

/**
 *
 * @author lennyn
 */
public class XModelImpl implements XModel {

	private final XView view;
	private final Semaphore execSemaphore = new Semaphore(0);
	private XmlHelper xmlHelper;
	private XWorker worker;

	public XModelImpl(XView view) {
		this.view = view;
		XConst.QUERY_FILES_DIR.mkdirs();
	}

	@Override
	public void processFiles() {
		view.clearErrors();
		view.clearResults();
		view.setRunning(true);
		worker = new SwingXWorker(this);
		execSemaphore.release();
		worker.scheduleProcessing();
	}

	private List<File> getFiles() {
		List<File> res = Lists.newLinkedList();
		File file = view.getInputRoot();
		if (file.isDirectory()) {
			FileFilter fileFilter = new RegexpFileFilter(view.getFileNamePattern());
			for (File f : getFilesFromDir(file, fileFilter)) {
				res.add(f);
			}
		}
		else if (file.isFile()) {
			res.add(file);
		}
		else {
			error(String.format("No such file: %s", file.getAbsoluteFile()));
		}
		Collections.sort(res);
		return res;
	}

	private List<File> getFilesFromDir(File file, FileFilter fileFilter) {

		List<File> res = new LinkedList<File>(Arrays.asList(file.listFiles(fileFilter)));
		for (File dir : file.listFiles(DirFileFilter.getInstance())) {
			res.addAll(getFilesFromDir(dir, fileFilter));
		}
		return res;
	}

	private XdmValue processFile(File file) throws Exception {
		XdmValue val = xmlHelper.execute(file);
		return val;
	}

	public void doPublishPartialResult(File file, XdmValue value) {
		view.addResult(file, value);
	}

	public void done() {
		view.log("DONE");
		view.setRunning(false);
		execSemaphore.drainPermits();
	}

	public void error(String msg) {
		view.log("FAILED");
		view.showError(msg);
		view.setRunning(false);
		execSemaphore.drainPermits();
	}

	private void checkIfNotStopped() throws StoppedException {
		if (!execSemaphore.tryAcquire())
			throw new StoppedException();
		else
			execSemaphore.release();
	}

	public void doProcessFiles() throws Exception {
		log("Compiling query...");
		if (view.getLanguageChoice() == XView.LanguageChoice.XQUERY) {
			xmlHelper = new XQueryHelper(view.getInputText(), view.getDefaultNamespace());
		}
		else if (view.getLanguageChoice() == XView.LanguageChoice.XPATH2) {
			xmlHelper = new XPathHelper(view.getInputText(), view.getDefaultNamespace());
		}

		checkIfNotStopped();

		log("Getting files list...");

		List<File> files = getFiles();

		for (File file : files) {
			log(String.format("Processing file: %s ...", file.getPath()));
			checkIfNotStopped();
			XdmValue value = processFile(file);
			worker.publishPartialResult(file, value);
		}
	}

	private void log(String msg) throws Exception {
		SwingUtilities.invokeAndWait(new LogRunnable(view, msg));
	}

	@Override
	public void stopProcessing() {
		execSemaphore.acquireUninterruptibly();
	}

	@Override
	public File getDefaultQueryFilesDirectory() {
		return XConst.QUERY_FILES_DIR;
	}

	@Override
	public File getDefaultDataDirectory() {
		throw new UnsupportedOperationException("Not supported yet.");
	}

	private final static class LogRunnable implements Runnable {
		private final XView view;
		private final String msg;

		public LogRunnable(XView view, String msg) {
			this.view = view;
			this.msg = msg;
		}

		public void run() {
			view.log(msg);
		}
	}
}
